home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
dev
/
sun4.md
/
devVMElink.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
41KB
|
1,357 lines
/*
* devVMElink.c --
*
* Routines used for running the Bit-3 VME-VME card cage link
* board set. These routines are to set up and handle a master
* board; a slave interface should require no (software) setup.
*
* Copyright 1990 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/sun4.md/devVMElink.c,v 1.7 92/10/23 15:04:40 elm Exp $ SPRITE (Berkeley)";
#endif /* not lint */
#include "sprite.h"
#include "stdio.h"
#include "mach.h"
#include "dev.h"
#include "devInt.h"
#include "stdlib.h"
#include "sync.h"
#include "vmMach.h"
#include "devVMElink.h"
#include "string.h"
#include "dbg.h"
/*
* Uncomment the following line of code to use the kernel call
* VmMach_MapInBigDevice. Currently, this call doesn't work.
*/
#define MAP_IN_BIG_DEVICE
static VMELinkInfo *VMEInfo[DEV_VMELINK_MAX_BOARDS];
static Boolean devVMElinkInitted = FALSE;
static int devVMElinkDebug = FALSE;
typedef void (*VoidFunc)();
typedef unsigned short uint16;
void DevVMElinkDmaDone _ARGS_((VMELinkInfo* linkInfo));
Boolean DevVMElinkXferData _ARGS_((VMELinkInfo* linkInfo));
/*
* These are the addresses for each card's "window" register access.
*/
unsigned int windowPhysAddr[] = {0xd0010000, 0xd0030000, 0xff810000,
#if 0
0xd0050000, 0xd0070000, 0xd0090000,
0xd00b0000, 0xd00d0000, 0xd00f0000,
0xd0110000, 0xd0130000, 0xd0150000,
#else
0, 0, 0,
0, 0, 0,
0, 0, 0,
#endif
};
/*
*----------------------------------------------------------------------
*
* DevVMElinkReset
*
* Reset the VME link board. Also set up any necessary registers
* with the proper values. If a hard reset is requested, or if
* the remote board is wedged, the remote card cage will be
* reset (VME SYSRESET).
*
* Returns:
* SUCCESS if reset worked, error code otherwise.
*
* Side effects:
* Resets the VME link board. Any currently-running operation is
* aborted.
*
*----------------------------------------------------------------------
*/
ReturnStatus
DevVMElinkReset (linkInfo, hardReset)
VMELinkInfo *linkInfo;
int hardReset;
{
unsigned char curStatus = DEV_VMELINK_REMOTE_DOWN;
unsigned char remoteStatus;
ReturnStatus status = SUCCESS;
CtrlRegs *regPtr = linkInfo->regArea;
MASTER_LOCK (&linkInfo->mutex);
if (devVMElinkDebug) {
printf ("DevVMElinkReset: starting reset.\n");
}
/*
* Clear all state except vme address space state bit and whether or
* not the link has memory mapped in.
*/
linkInfo->state &= (DEV_VMELINK_STATE_VME_A32 | DEV_VMELINK_STATE_NO_MAP);
linkInfo->position = 0;
status = Mach_Probe (sizeof (curStatus), (char *)&(regPtr->LocalStatus),
(char *)&curStatus);
if (devVMElinkDebug) {
printf ("DevVMElinkReset: Status was 0x%x\n", curStatus);
}
if (status != SUCCESS) {
printf("DevVMElinkReset: local board not responding.\n");
goto resetExit;
}
if (!(curStatus & DEV_VMELINK_REMOTE_DOWN)) {
/*
* Try to read the remote status register. If we can't do it,
* reset the remote card cage whether or not it was requested.
*/
if (Mach_Probe (sizeof (remoteStatus), (char *)&(regPtr->RemoteCmd1),
(char *)&remoteStatus) != SUCCESS) {
hardReset = TRUE;
}
/*
* Reset the remote card cage if requested or needed.
*/
if (hardReset) {
printf ("DevVMElinkReset: Resetting remote VME link card...\n");
regPtr->RemoteCmd1 = DEV_VMELINK_REMOTE_RESET;
if (devVMElinkDebug) {
printf ("DevVMElinkReset: Just reset remote board.\n");
}
MACH_DELAY(1000000);
}
remoteStatus = regPtr->RemoteCmd1;
} else {
printf ("DevVMElinkReset: Remote VME link down; can't access.\n");
}
regPtr->LocalCmd = DEV_VMELINK_CLEAR_LOCAL_ERRS;
curStatus = regPtr->LocalStatus;
if (devVMElinkDebug) {
printf ("DevVMElinkReset: Status is now 0x%x (after errs cleared)\n",
curStatus);
}
regPtr->LocalIntrVec = (unsigned char)linkInfo->vectorNumber;
resetExit:
if (devVMElinkDebug) {
printf ("Just before putting in local flags, status is 0x%x\n",
regPtr->LocalStatus);
}
linkInfo->LocalFlags = 0;
regPtr->LocalCmd = linkInfo->LocalFlags;
linkInfo->RemoteFlags1 = 0;
linkInfo->RemoteFlags2 = 0 /*DEV_VMELINK_REMOTE_PAUSE_16*/;
if (!(curStatus & DEV_VMELINK_REMOTE_DOWN)) {
if (devVMElinkDebug) {
printf ("Just before putting in remote flags, status is 0x%x\n",
regPtr->LocalStatus);
}
regPtr->RemoteCmd1 = linkInfo->RemoteFlags1;
regPtr->RemoteCmd2 = linkInfo->RemoteFlags2;
} else {
if (devVMElinkDebug) {
printf ("DevVMElinkReset: remote down; not initted cmd regs\n");
}
}
MASTER_UNLOCK (&linkInfo->mutex);
return(status);
}
/***********************************************************************
*
* DevVMElinkAttach
*
* Attach a VME link board as a block device.
*
* Returns: block device handle
*
***********************************************************************
*/
DevBlockDeviceHandle *
DevVMElinkAttach (devPtr)
Fs_Device *devPtr;
{
DevVMElinkHandle *handlePtr;
VMELinkInfo *linkInfo;
if (devPtr->unit >= DEV_VMELINK_MAX_BOARDS ||
((devPtr->data = (ClientData)(VMEInfo[devPtr->unit])) == NULL)) {
return ((DevBlockDeviceHandle *)NIL);
}
linkInfo = (VMELinkInfo *)(devPtr->data);
handlePtr = (DevVMElinkHandle *) malloc (sizeof (DevVMElinkHandle));
if (handlePtr == NULL) {
return ((DevBlockDeviceHandle *)NIL);
}
handlePtr->blockHandle.blockIOProc = DevVMElinkBlockIO;
handlePtr->blockHandle.IOControlProc = DevVMElinkBlockIOControl;
handlePtr->blockHandle.releaseProc = DevVMElinkRelease;
handlePtr->blockHandle.minTransferUnit = sizeof (int);
handlePtr->blockHandle.maxTransferSize = DEV_VMELINK_MAX_TRANSFER_SIZE;
handlePtr->linkInfo = linkInfo;
handlePtr->magic = DEV_VMELINK_HANDLE_MAGIC;
MASTER_LOCK (&(linkInfo->mutex));
linkInfo->numAttached += 1;
MASTER_UNLOCK (&(linkInfo->mutex));
return ((DevBlockDeviceHandle *)handlePtr);
}
/***********************************************************************
*
* DevVMElinkRelease
*
* Release the resources used by an attach(). Currently, this
* means decrementing the count of attachers.
*
***********************************************************************
*/
ReturnStatus
DevVMElinkRelease (devHandle)
DevVMElinkHandle *devHandle;
{
VMELinkInfo *linkInfo;
if (devHandle->magic != DEV_VMELINK_HANDLE_MAGIC) {
printf ("DevVMElinkRelease: bad handle magic #: 0x%x\n",
devHandle->magic);
return (FAILURE);
}
linkInfo = devHandle->linkInfo;
MASTER_LOCK (&(linkInfo->mutex));
linkInfo->numAttached -= 1;
MASTER_UNLOCK (&(linkInfo->mutex));
free ((char *)devHandle);
return (SUCCESS);
}
/***********************************************************************
*
* DevVMElinkBlockIO
*
* This routine copies all the information into a VME transfer
* request block. It then queues up the I/O by calling DevQueue.
*
* Returns: standard Sprite return status
*
* Side effects: Queues up a request for a VME transfer
*
***********************************************************************
*/
ReturnStatus
DevVMElinkBlockIO (devHandle, devReqPtr)
DevVMElinkHandle *devHandle;
DevBlockDeviceRequest *devReqPtr;
{
DevVMElinkReq *req;
VMELinkInfo* linkInfo;
req = (DevVMElinkReq *)devReqPtr->ctrlData;
linkInfo = req->linkInfo = devHandle->linkInfo;
if (linkInfo->state & DEV_VMELINK_STATE_NO_MAP) {
return (FAILURE);
}
List_InitElement ((List_Links *)&(req->links));
req->origReq = devReqPtr;
req->startAddress = devReqPtr->startAddress;
req->length = devReqPtr->bufferLen;
req->buffer = devReqPtr->buffer;
req->operation = devReqPtr->operation;
req->status = SUCCESS;
MASTER_LOCK (&linkInfo->mutex);
List_Insert ((List_Links*)req, LIST_ATREAR (&linkInfo->reqHdr));
if (!(linkInfo->state & DEV_VMELINK_STATE_DMA_IN_USE)) {
DevVMElinkXferData (linkInfo);
}
MASTER_UNLOCK (&linkInfo->mutex);
return (SUCCESS);
}
/***********************************************************************
*
* DevVMElinkDmaDone
*
* Side effects:
* Copies data over the VME link. May affect the device queues by
* servicing the next request.
*
***********************************************************************
*/
void
DevVMElinkDmaDone (linkInfo)
VMELinkInfo* linkInfo;
{
register DevVMElinkReq* reqPtr;
MASTER_LOCK (&linkInfo->mutex);
reqPtr = (DevVMElinkReq*)List_First (&linkInfo->reqHdr);
List_Remove ((List_Links*)reqPtr);
if (reqPtr->dmaSize > 0) {
VmMach_DMAFree (reqPtr->dmaSize, (Address)(reqPtr->dmaSpace));
}
linkInfo->regArea->RemoteCmd2 = linkInfo->RemoteFlags2;
linkInfo->state &= ~DEV_VMELINK_STATE_DMA_IN_USE;
/*
* Check to see if there's another request outstanding. If there
* is, start it up.
*/
if (!List_IsEmpty ((List_Links*)&linkInfo->reqHdr)) {
DevVMElinkXferData (linkInfo);
}
MASTER_UNLOCK (&linkInfo->mutex);
(reqPtr->origReq->doneProc)(reqPtr->origReq, reqPtr->status,
reqPtr->length);
}
/*
*----------------------------------------------------------------------
*
* DevVMElinkXferData
*
* Transfer data over the VME link. If there is a small amount
* of data, just copy it over word by word. If the amount is
* larger than 256, copy most of it by DMA and the remainder
* word by word. NOTE: the transfer must be word-aligned on
* both sides. If not, the routine returns an error.
*
* Since the link can't be multiplexed, transfers queue up
* at the start of this routine.
*
* The master lock for this interface must be held *before* this
* routine is called.
*
* Returns:
* TRUE if the request was processed; FALSE otherwise.
*
* Side effects:
* Data is transferred over the VME link boards.
*
*----------------------------------------------------------------------
*/
static
Boolean
DevVMElinkXferData (linkInfo)
VMELinkInfo* linkInfo;
{
DevVMElinkReq* reqPtr = (DevVMElinkReq*)List_First (&(linkInfo->reqHdr));
int bufSize;
unsigned long remoteAddr;
unsigned long bufAddr;
unsigned long dmaAddr;
int dmaSize = 0;
int copySize;
unsigned char dmaCmd;
DmaRegs *dmaPtr = linkInfo->dmaRegs;
register CtrlRegs* regPtr = linkInfo->regArea;
linkInfo->curReq = reqPtr;
bufSize = reqPtr->length;
if (devVMElinkDebug) {
printf ("VMElink: Trying I/O at 0x%x for 0x%x bytes.\n",
linkInfo->position, bufSize);
}
/*
* First, copy the data that won't be DMAed.
*/
linkInfo->state |= DEV_VMELINK_STATE_DMA_IN_USE;
if (bufSize >= linkInfo->minDmaSize) {
dmaSize = bufSize & DEV_VMELINK_DMA_BUFSIZE_MASK;
} else {
dmaSize = 0;
}
copySize = bufSize - dmaSize;
if (devVMElinkDebug) {
printf("DevVMElinkXferData: bufsize=0x%x dmaSize=0x%x copySize=0x%x\n",
bufSize, dmaSize, copySize);
}
remoteAddr = (unsigned int)linkInfo->position + dmaSize;
bufAddr = (unsigned int)(reqPtr->buffer + dmaSize);
dmaAddr = (unsigned int)(linkInfo->smallMap + (remoteAddr & 0xffff));
regPtr->RemoteCmd1 = linkInfo->RemoteFlags1 | DEV_VMELINK_USE_PAGE_REG;
regPtr->RemotePageAddrHigh = ((unsigned int)remoteAddr >> 24) & 0xff;
regPtr->RemotePageAddrLow = ((unsigned int)remoteAddr >> 16) & 0xff;
if (reqPtr->operation == FS_READ) {
while (copySize > 0) {
*(int *)bufAddr = *(int *)dmaAddr;
dmaAddr += 4;
bufAddr += 4;
copySize -= 4;
remoteAddr += 4;
if (((unsigned int)remoteAddr & 0xffff) == 0) {
regPtr->RemotePageAddrHigh =
((unsigned int)remoteAddr >> 24) & 0xff;
regPtr->RemotePageAddrLow =
((unsigned int)remoteAddr >> 16) & 0xff;
}
}
} else {
while (copySize > 0) {
*(int *)dmaAddr = *(int *)bufAddr;
dmaAddr += 4;
bufAddr += 4;
copySize -= 4;
remoteAddr += 4;
if (((unsigned int)remoteAddr & 0xffff) == 0) {
regPtr->RemotePageAddrHigh =
((unsigned int)remoteAddr >> 24) & 0xff;
regPtr->RemotePageAddrLow =
((unsigned int)remoteAddr >> 16) & 0xff;
}
}
}
regPtr->RemoteCmd1 = linkInfo->RemoteFlags1;
if (bufSize >= linkInfo->minDmaSize) {
/*
* Map in buffer for DMA, and copy in/out a multiple of
* 256 bytes. The remainder will be copied over later.
*/
bufAddr = (unsigned int)reqPtr->buffer;
remoteAddr = linkInfo->position;
dmaAddr = (unsigned int)VmMach_DMAAlloc(dmaSize,(Address)bufAddr);
if (dmaAddr == 0) {
panic ("DevVMElinkXferData: couldn't allocate DMA space.\n");
}
if (devVMElinkDebug) {
printf ("DevVMElinkXferData: DMA mapped to 0x%x\n", dmaAddr);
}
reqPtr->dmaSpace = (Address)dmaAddr;
reqPtr->dmaSize = dmaSize;
/*
* gets user address modifier.
* requires addr mod 0x3d (for A24) or 0x0d (for A32) on link board
* Convert dmaAddr to a VME address.
*/
dmaAddr &= 0x000fffff;
if (linkInfo->state & DEV_VMELINK_STATE_VME_A32) {
regPtr->LocalAddrMod = 0x0d;
if (linkInfo->RemoteFlags2 & DEV_VMELINK_REMOTE_DMA_BLOCK_MODE) {
regPtr->RemoteAddrMod = 0x0f;
} else {
regPtr->RemoteAddrMod = 0x0d;
}
} else {
regPtr->LocalAddrMod = 0x3d;
if (linkInfo->RemoteFlags2 & DEV_VMELINK_REMOTE_DMA_BLOCK_MODE) {
regPtr->RemoteAddrMod = 0x3f;
} else {
regPtr->RemoteAddrMod = 0x3d;
}
}
#if 1
dmaPtr->localDmaAddr3 = (dmaAddr >> 24) & 0xff;
dmaPtr->localDmaAddr2 = (dmaAddr >> 16) & 0xff;
dmaPtr->localDmaAddr1 = (dmaAddr >> 8) & 0xff;
dmaPtr->localDmaAddr0 = (dmaAddr ) & 0xff;
dmaPtr->remoteDmaAddr3 = (remoteAddr >> 24) & 0xff;
dmaPtr->remoteDmaAddr2 = (remoteAddr >> 16) & 0xff;
dmaPtr->remoteDmaAddr1 = (remoteAddr >> 8) & 0xff;
dmaPtr->remoteDmaAddr0 = (remoteAddr ) & 0xff;
dmaPtr->dmaLength2 = (dmaSize >> 16) & 0xff;
dmaPtr->dmaLength1 = (dmaSize >> 8) & 0xff;
#else
*(uint16*)dmaPtr->localDmaAddr3 = (uint16)((dmaAddr >> 16) & 0xffff);
*(uint16*)dmaPtr->localDmaAddr1 = (uint16)(dmaAddr & 0xffff);
*(uint16*)dmaPtr->remoteDmaAddr3 = (uint16)((remoteAddr>>16) & 0xffff);
*(uint16*)dmaPtr->remoteDmaAddr1 = (uint16)(remoteAddr & 0xffff);
*(uint16*)dmaPtr->dmaLength2 = (uint16)((dmaSize >> 8) & 0xffff);
#endif
regPtr->LocalCmd = linkInfo->LocalFlags |
DEV_VMELINK_LOCAL_DISABLE_INT;
regPtr->RemoteCmd2 = linkInfo->RemoteFlags2 |
DEV_VMELINK_REMOTE_DISABLE_INT;
dmaCmd = DEV_VMELINK_DMA_START | DEV_VMELINK_DMA_LONGWORD |
DEV_VMELINK_DMA_LOCAL_PAUSE | DEV_VMELINK_DMA_ENABLE_INTR
/* | DEV_VMELINK_DMA_BLOCK_MODE */ ;
dmaCmd |= (reqPtr->operation == FS_WRITE) ?
DEV_VMELINK_DMA_LOCAL_TO_REMOTE : DEV_VMELINK_DMA_REMOTE_TO_LOCAL;
if (devVMElinkDebug) {
printf ("DevVMElinkXferData: Setting dma cmd for %s to 0x%x\n",
linkInfo->name, dmaCmd);
}
dmaPtr->localDmaCmdReg = dmaCmd;
/*
* DevVMElinkDmaDone will be called after the interrupt is taken.
* It will also check to see if there are more entries waiting in
* the device queue.
*/
} else {
reqPtr->dmaSpace = (Address)NIL;
reqPtr->dmaSize = 0;
Proc_CallFunc ((VoidFunc)DevVMElinkDmaDone, (ClientData)linkInfo, 0);
}
return (TRUE);
}
/*----------------------------------------------------------------------
*
* DevVMElinkIntr
*
* Called when an interrupt from the link card is received. This
* should only happen when a DMA operation finishes. Interrupts
* from a remote card cage should be handled in the (separate)
* device driver for the remote device.
*
* Results:
* TRUE if this link card caused the interrupt.
*
* Side effects:
* Notifies any waiting processes, and clears the interrupt bits
* on the link card.
*
*----------------------------------------------------------------------
*/
Boolean
DevVMElinkIntr (data)
ClientData data;
{
VMELinkInfo *linkInfo = (VMELinkInfo *)data;
DmaRegs *dmaRegs;
CtrlRegs *regPtr;
Boolean retVal = FALSE;
unsigned char tmpReg;
dmaRegs = linkInfo->dmaRegs;
regPtr = linkInfo->regArea;
MASTER_LOCK (&linkInfo->mutex);
tmpReg = dmaRegs->localDmaCmdReg;
if (devVMElinkDebug) {
printf ("Got intr for %s (dmareg = 0x%x)\n", linkInfo->name, tmpReg);
}
if (tmpReg & DEV_VMELINK_DMA_DONE) {
tmpReg &= ~(DEV_VMELINK_DMA_DONE | DEV_VMELINK_DMA_START);
dmaRegs->localDmaCmdReg = tmpReg;
/*
* Re-enable interrupts.
*/
regPtr->LocalCmd = linkInfo->LocalFlags;
regPtr->RemoteCmd2 = linkInfo->RemoteFlags2;
if (!(linkInfo->state & DEV_VMELINK_STATE_DMA_IN_USE)) {
if (devVMElinkDebug) {
printf ("DevVMElinkIntr: DMA finished but shouldn't have\n");
}
if (linkInfo->curReq != NULL) {
linkInfo->curReq->status = FAILURE;
}
}
if (List_IsEmpty (&linkInfo->reqHdr)) {
panic ("DevVMElinkFinishCopy: no current I/O\n");
}
/*
* Callback DevVMElinkDmaDone to finish the copy and schedule the
* next one if necessary.
*/
Proc_CallFunc ((VoidFunc)DevVMElinkDmaDone, (ClientData)linkInfo, 0);
retVal = TRUE;
} else if (devVMElinkDebug) {
printf ("DevVMElinkIntr: bogus interrupt?\n");
}
MASTER_UNLOCK (&linkInfo->mutex);
return (retVal);
}
/***********************************************************************
*
* setRemotePage
*
* Set the bits which supply the remote page address to those
* passed. Turn on the flag which tells the link board to use
* those bits.
*
***********************************************************************
*/
static
void
setRemotePage (linkInfo, pageNum)
register VMELinkInfo *linkInfo;
unsigned int pageNum;
{
register CtrlRegs *regPtr = linkInfo->regArea;
if (devVMElinkDebug) {
printf ("Setting remote VME page to 0x%04x\n", pageNum);
}
MASTER_LOCK (&linkInfo->mutex);
linkInfo->RemoteFlags1 |= DEV_VMELINK_USE_PAGE_REG;
regPtr->RemoteCmd1 = linkInfo->RemoteFlags1;
regPtr->RemotePageAddrHigh = (unsigned char)((pageNum >> 8) & 0xff);
regPtr->RemotePageAddrLow = (unsigned char)(pageNum & 0xff);
linkInfo->state |= DEV_VMELINK_STATE_PAGE_MODE;
if (devVMElinkDebug) {
printf ("Page set to 0x%02x%02x; flags are 0x%x\n",
regPtr->RemotePageAddrHigh, regPtr->RemotePageAddrLow,
linkInfo->RemoteFlags1);
}
MASTER_UNLOCK (&linkInfo->mutex);
}
/***********************************************************************
*
* turnOffPageMode
*
* Turn off page mode as established using setRemotePage.
*
***********************************************************************
*/
static
void
turnOffPageMode (linkInfo)
register VMELinkInfo *linkInfo;
{
MASTER_LOCK (&linkInfo->mutex);
linkInfo->RemoteFlags1 &= ~DEV_VMELINK_USE_PAGE_REG;
linkInfo->regArea->RemoteCmd1 = linkInfo->RemoteFlags1;
linkInfo->state &= ~DEV_VMELINK_STATE_PAGE_MODE;
MASTER_UNLOCK (&linkInfo->mutex);
}
/***********************************************************************
*
* setAddrModifier --
*
* Set the address modifier to whatever's passed, and enable its
* use on the board.
*
* Returns:
* none
* Side effects:
* Future VME accesses will use this address modifier.
*
***********************************************************************
*/
static
void
setAddrModifier (linkInfo, mod)
register VMELinkInfo* linkInfo;
unsigned int mod;
{
linkInfo->RemoteFlags2 |= DEV_VMELINK_REMOTE_USE_ADDRMOD;
linkInfo->regArea->RemoteAddrMod = (unsigned char) mod;
linkInfo->regArea->RemoteCmd2 = linkInfo->RemoteFlags2;
if (devVMElinkDebug) {
printf ("VME address modifier set to 0x%02x\n", mod);
}
}
/***********************************************************************
*
* turnOffAddrModifier --
*
* Turn off the remote address modifier.
*
* Returns:
* none
* Side Effects:
* The address modifier register is no longer used for remote
* accesses.
*
***********************************************************************
*/
static
void
turnOffAddrModifier (linkInfo)
register VMELinkInfo* linkInfo;
{
linkInfo->RemoteFlags2 &= ~DEV_VMELINK_REMOTE_USE_ADDRMOD;
linkInfo->regArea->RemoteCmd2 = linkInfo->RemoteFlags2;
}
/***********************************************************************
*
* DevVMElinkAccessRemoteMemory --
*
* Access memory across the link board. The accesses are limited
* to 4000 bytes, since that's all an ioctl will copy in or out
* (with the necessary header). The bytes must all be consecutive,
* and must be long-word sized and aligned.
*
***********************************************************************
*/
ReturnStatus
DevVMElinkAccessRemoteMemory (ioctlPtr, linkInfo)
register Fs_IOCParam *ioctlPtr;
register VMELinkInfo *linkInfo;
{
int inSize, outSize;
int fmtStatus;
ReturnStatus status = SUCCESS;
DevVMElinkAccessMem memAccess;
unsigned int buf[1000];
unsigned int *remote, *local;
unsigned int remotePage, remoteOffsetInPage;
int nbytes;
if (linkInfo->state & DEV_VMELINK_STATE_NO_MAP) {
status = FAILURE;
goto accessExit;
}
inSize = ioctlPtr->inBufSize;
outSize = sizeof (memAccess) - sizeof (memAccess.data);
fmtStatus = Fmt_Convert ("www", ioctlPtr->format, &inSize,
ioctlPtr->inBuffer, mach_Format, &outSize,
(Address)&memAccess);
if (fmtStatus != FMT_OK) {
printf ("DevVMElinkAccessRemoteMemory:cmd format failed, 0x%x\n",
fmtStatus);
status = GEN_INVALID_ARG;
goto accessExit;
}
if (memAccess.size > sizeof (buf)) {
printf ("DevVMElinkAccessRemoteMemory: too much data (0x%x bytes)\n",
memAccess.size);
goto accessExit;
}
if (devVMElinkDebug) {
printf ("DevVMElinkAccessRemoteMemory: trying 0x%x\n",
memAccess.destAddress);
printf ("DevVMElinkAccessRemoteMemory: map at virtual 0x%x\n",
linkInfo->smallMap);
}
remoteOffsetInPage = (((unsigned int)memAccess.destAddress) & 0xffff);
if ((remoteOffsetInPage & 0x3) != 0) {
printf ("DevVMElinkAccessRemoteMemory: remote addr (0x%x) unaligned\n",
memAccess.destAddress);
goto accessExit;
}
remotePage = ((unsigned int)memAccess.destAddress >> 16) & 0xffff;
remote = (unsigned int *)(linkInfo->smallMap + remoteOffsetInPage);
setRemotePage (linkInfo, remotePage);
setAddrModifier (linkInfo, linkInfo->curAddrModifier);
local = buf;
if (devVMElinkDebug) {
printf ("DevVMELinkAccessRemoteMemory: local 0x%x, remote 0x%x\n",
local, remote);
printf ("DevVMELinkAccessRemoteMemory: page offset 0x%x\n",
remoteOffsetInPage);
}
if (memAccess.direction == DEV_VMELINK_TO_REMOTE) {
inSize = memAccess.size;
outSize = sizeof (buf);
fmtStatus = Fmt_Convert
("w*", ioctlPtr->format, &inSize,
(Address)(((DevVMElinkAccessMem*)(ioctlPtr->inBuffer))->data),
mach_Format, &outSize, (Address)buf);
if (fmtStatus != FMT_OK) {
printf ("DevVMElinkAccessRemoteMemory:data format failed, 0x%x\n",
fmtStatus);
status = GEN_INVALID_ARG;
goto accessExit;
}
for (nbytes = 0; nbytes < memAccess.size; nbytes += sizeof (int)) {
if (linkInfo->state & DEV_VMELINK_STATE_SAFE_COPY) {
status = Mach_Probe (sizeof (int), (Address)(local++),
(Address)(remote++));
if (status != SUCCESS) {
if (devVMElinkDebug) {
printf ("%s: Writing to VME addr 0x%x failed.\n",
linkInfo->name, --remote);
}
goto accessExit;
}
} else {
*(remote++) = *(local++);
}
remoteOffsetInPage += sizeof (int);
if ((remoteOffsetInPage & 0xffff) == 0) {
remote = (unsigned int *)linkInfo->smallMap;
remotePage += 1;
setRemotePage (linkInfo, remotePage);
}
}
} else if (memAccess.direction == DEV_VMELINK_TO_LOCAL) {
for (nbytes = 0; nbytes < memAccess.size; nbytes += sizeof (int)) {
if (linkInfo->state & DEV_VMELINK_STATE_SAFE_COPY) {
status = Mach_Probe (sizeof (int), (Address)(remote++),
(Address)(local++));
if (status != SUCCESS) {
if (devVMElinkDebug) {
printf ("%s: Reading from VME addr 0x%x failed.\n",
linkInfo->name, --remote);
}
goto accessExit;
}
} else {
*(local++) = *(remote++);
}
remoteOffsetInPage += sizeof (int);
if ((remoteOffsetInPage & 0xffff) == 0) {
remote = (unsigned int *)linkInfo->smallMap;
remotePage += 1;
setRemotePage (linkInfo, remotePage);
}
}
inSize = memAccess.size;
outSize = ioctlPtr->outBufSize + sizeof (memAccess.data) -
sizeof (memAccess);
fmtStatus = Fmt_Convert
("w*", mach_Format, &inSize,(Address)buf,ioctlPtr->format,&outSize,
(Address)(((DevVMElinkAccessMem*)(ioctlPtr->outBuffer))->data));
if (fmtStatus != FMT_OK) {
printf ("DevVMElinkAccessRemoteMemory:data format failed, 0x%x\n",
fmtStatus);
status = GEN_INVALID_ARG;
goto accessExit;
}
} else {
printf ("DevVMElinkAccessRemoteMemory: illegal direction 0x%x\n",
memAccess.direction);
status = GEN_INVALID_ARG;
goto accessExit;
}
accessExit:
turnOffPageMode (linkInfo);
turnOffAddrModifier (linkInfo);
return (status);
}
/***********************************************************************
*
* DevVMElinkBlockIOControl
*
* This is the main IOControl routine for the VMElink driver.
* It's easier to fake a "standard" IOControl using the block
* driver than vice versa, since the standard IOControl has an
* easy way of getting a real handle, but the BlockIOControl
* has no way of getting an Fs_Device.
*
* Returns: standard Sprite return status
*
* Side effects: depends on IOControl
*
***********************************************************************
*/
ENTRY ReturnStatus
DevVMElinkBlockIOControl (handlePtr, ioctlPtr, replyPtr)
DevVMElinkHandle *handlePtr;
register Fs_IOCParam *ioctlPtr;
register Fs_IOReply *replyPtr;
/* ARGSUSED */
{
ReturnStatus status = SUCCESS;
register volatile CtrlRegs *regPtr;
register VMELinkInfo *linkData;
unsigned int passedData;
DevVMElinkStatus *boardStatus;
int inSize, outSize;
int fmtStatus;
if (devVMElinkDebug) {
printf ("VMElink: doing IOControl 0x%x\n", ioctlPtr->command);
}
linkData = handlePtr->linkInfo;
regPtr = linkData->regArea;
/*
* If the remote board isn't working, don't even bother with the
* IOcontrol call and just return failure.
*/
if ((ioctlPtr->command != IOC_VMELINK_DEBUG_ON) &&
(ioctlPtr->command != IOC_VMELINK_DEBUG_OFF) &&
(regPtr->RemoteCmd1 & DEV_VMELINK_REMOTE_DOWN)) {
return (DEV_OFFLINE);
}
switch (ioctlPtr->command) {
case IOC_VMELINK_STATUS:
boardStatus =(DevVMElinkStatus *)ioctlPtr->inBuffer;
boardStatus->LocalStatus = (int)regPtr->LocalStatus;
boardStatus->RemoteStatus = (int)regPtr->RemoteCmd1;
break;
case IOC_VMELINK_SET_ADDRMOD:
passedData = *(unsigned int *)ioctlPtr->inBuffer;
linkData->curAddrModifier = (unsigned char)passedData;
break;
case IOC_VMELINK_SET_MIN_DMA_SIZE:
outSize = sizeof (linkData->minDmaSize);
inSize = ioctlPtr->inBufSize;
fmtStatus = Fmt_Convert ("w", ioctlPtr->format, &inSize,
(Address)ioctlPtr->inBuffer, mach_Format,
&outSize, (Address)&(linkData->minDmaSize));
if (fmtStatus != FMT_OK) {
printf ("Format of VMELINK_SET_MIN_DMA_SIZE failed, 0x%x\n",
fmtStatus);
return GEN_INVALID_ARG;
}
if (devVMElinkDebug) {
printf ("VMELINK_SET_MIN_DMA_SIZE: min DMA size set to 0x%x\n",
linkData->minDmaSize);
}
break;
#if 0
case IOC_VMELINK_NO_ADDRMOD:
linkData->RemoteFlags2 &= ~DEV_VMELINK_REMOTE_USE_ADDRMOD;
regPtr->RemoteCmd2 = linkData->RemoteFlags2;
break;
case IOC_VMELINK_LOW_VME:
linkData->addrMsb = 0x00000000;
break;
case IOC_VMELINK_HIGH_VME:
linkData->addrMsb = 0x80000000;
break;
#endif
case IOC_VMELINK_DEBUG_ON:
devVMElinkDebug = TRUE;
break;
case IOC_VMELINK_DEBUG_OFF:
devVMElinkDebug = FALSE;
break;
case IOC_VMELINK_RESET:
status = DevVMElinkReset (linkData, TRUE);
break;
case IOC_VMELINK_SAFE_COPY_ON:
linkData->state |= DEV_VMELINK_STATE_SAFE_COPY;
break;
case IOC_VMELINK_SAFE_COPY_OFF:
linkData->state &= ~DEV_VMELINK_STATE_SAFE_COPY;
break;
case IOC_VMELINK_REMOTE_BLOCK_MODE_ON:
linkData->RemoteFlags2 |= DEV_VMELINK_REMOTE_DMA_BLOCK_MODE;
break;
case IOC_VMELINK_REMOTE_BLOCK_MODE_OFF:
linkData->RemoteFlags2 &= ~DEV_VMELINK_REMOTE_DMA_BLOCK_MODE;
break;
case IOC_VMELINK_READ_BOARD_STATUS:
{
unsigned char regs[0x20];
regs[0x1] = regPtr->LocalCmd;
regs[0x3] = regPtr->LocalStatus;
regs[0x9] = regPtr->RemoteCmd1;
regs[0x8] = regPtr->RemoteCmd2;
regs[0x10] = linkData->dmaRegs->localDmaCmdReg;
inSize = sizeof (regs);
outSize = ioctlPtr->outBufSize;
fmtStatus = Fmt_Convert ("b32", mach_Format, &inSize,
(Address)regs, ioctlPtr->format, &outSize,
(Address)ioctlPtr->outBuffer);
if (fmtStatus != FMT_OK) {
printf ("Format of VMELINK_READ_BOARD_STATUS failed, 0x%x\n",
fmtStatus);
return GEN_INVALID_ARG;
}
}
break;
case IOC_VMELINK_ACCESS_REMOTE_MEMORY:
status = DevVMElinkAccessRemoteMemory (ioctlPtr, linkData);
break;
case IOC_VMELINK_WRITE_REG:
{
unsigned char stuff[2];
inSize = ioctlPtr->inBufSize;
outSize = sizeof (stuff);
fmtStatus = Fmt_Convert ("b2", ioctlPtr->format, &inSize,
(Address)ioctlPtr->inBuffer, mach_Format,
&outSize, (Address)stuff);
if (fmtStatus != FMT_OK) {
printf ("Format of VMELINK_WRITE_REG failed, 0x%x\n",
fmtStatus);
return GEN_INVALID_ARG;
}
status = Mach_Probe (sizeof (stuff[1]), (Address)&(stuff[1]),
(Address)regPtr + stuff[0]);
}
break;
case IOC_VMELINK_READ_REG:
{
unsigned char stuff[2];
inSize = ioctlPtr->inBufSize;
outSize = sizeof (stuff);
fmtStatus = Fmt_Convert ("b2", ioctlPtr->format, &inSize,
(Address)ioctlPtr->inBuffer, mach_Format,
&outSize, (Address)stuff);
if (fmtStatus != FMT_OK) {
printf ("Format of VMELINK_READ_REG failed, 0x%x\n",fmtStatus);
return GEN_INVALID_ARG;
}
status = Mach_Probe (sizeof (stuff[1]), (Address)regPtr + stuff[0],
(Address)&(stuff[1]));
inSize = sizeof (stuff);
outSize = ioctlPtr->outBufSize;
fmtStatus = Fmt_Convert ("b2", mach_Format, &inSize,(Address)stuff,
ioctlPtr->format, &outSize,
(Address)ioctlPtr->outBuffer);
if (fmtStatus != FMT_OK) {
printf ("Format of VMELINK_READ_REG failed, 0x%x\n",fmtStatus);
return GEN_INVALID_ARG;
}
}
break;
case IOC_VMELINK_PING_REMOTE:
/*
* Do nothing here; the null call will tell whether the remote
* board has been turned on or not.
*/
break;
case IOC_LOCK:
case IOC_UNLOCK:
return (GEN_NOT_IMPLEMENTED);
break;
case IOC_REPOSITION:
{
Ioc_RepositionArgs pos;
unsigned int curAddr;
inSize = ioctlPtr->inBufSize;
outSize = sizeof (pos);
fmtStatus = Fmt_Convert ("ww", ioctlPtr->format, &inSize,
ioctlPtr->inBuffer, mach_Format,
&outSize, (Address)&pos);
if (fmtStatus != FMT_OK) {
printf ("DevVMElinkIOControl: IOC 0x%x error 0x%x\n",
ioctlPtr->command, fmtStatus);
return (GEN_INVALID_ARG);
}
switch (pos.base) {
case IOC_BASE_ZERO:
curAddr = 0x0;
break;
case IOC_BASE_CURRENT:
curAddr = linkData->position;
break;
case IOC_BASE_EOF:
curAddr = 0x0;
break;
default:
return (GEN_INVALID_ARG);
break;
}
if (pos.offset < 0) {
linkData->position = curAddr - (unsigned int)(-pos.offset);
} else {
linkData->position = curAddr + (unsigned int)pos.offset;
}
if (devVMElinkDebug) {
printf ("VMElink: new position is 0x%x\n", linkData->position);
}
}
break;
case IOC_GET_FLAGS:
case IOC_SET_FLAGS:
case IOC_SET_BITS:
case IOC_CLEAR_BITS:
break;
default:
return (GEN_NOT_IMPLEMENTED);
break;
}
return (status);
}
/*
*----------------------------------------------------------------------
*
* DevVMElinkIOControl
*
* Perform an IO control on the VME link card set. These can
* range from a simple remote ping to playing with the card's
* registers such as address modifiers or window selection.
* No attempt is made to coordinate between two processes that
* mess up the same VME link; that may come in a future version.
*
* Results:
*
*
* Side effects:
* May map additional VME address space into the kernel.
* Other side effects depending on the ioctl call requested.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
DevVMElinkIOControl (devicePtr, ioctlPtr, replyPtr)
Fs_Device *devicePtr; /* Information about device. */
Fs_IOCParam *ioctlPtr; /* Parameter information (buffer sizes etc.). */
Fs_IOReply *replyPtr; /* Place to store result information.*/
{
VMELinkInfo *linkInfo;
if (devVMElinkDebug) {
printf ("DevVMElinkIOControl: called for unit %d\n", devicePtr->unit);
}
if ((devicePtr->unit >= DEV_VMELINK_MAX_BOARDS) ||
((linkInfo = VMEInfo[devicePtr->unit]) == NULL)) {
return (DEV_INVALID_UNIT);
}
return (DevVMElinkBlockIOControl(&(linkInfo->handle), ioctlPtr, replyPtr));
}
/*
*----------------------------------------------------------------------
*
* DevVMElinkInit
*
* Initialize the VME link board (assuming that there is one
* installed).
*
*
* Results:
* Returns the "device number" of the VME link.
*
* Side effects:
* May map additional VME address space into the kernel.
*
*----------------------------------------------------------------------
*/
ENTRY
ClientData
DevVMElinkInit(cntrlPtr)
DevConfigController *cntrlPtr;
{
CtrlRegs *regPtr = (CtrlRegs *)cntrlPtr->address;
register VMELinkInfo *linkInfo;
char semName[40];
int linkNum;
ReturnStatus status;
DevBlockDeviceHandle *blockHandle;
if (!devVMElinkInitted) {
int i;
for (i = 0; i < DEV_VMELINK_MAX_BOARDS; i++) {
VMEInfo[i] = NULL;
}
devVMElinkInitted = TRUE;
}
/*
* If the VME link board isn't installed or there are too many boards,
* just return.
*/
linkNum = cntrlPtr->controllerID;
if (linkNum >= DEV_VMELINK_MAX_BOARDS ||
(Mach_Probe(sizeof(regPtr->LocalStatus), (char*)&(regPtr->LocalStatus),
(char *)&status) != SUCCESS)) {
printf ("%s (VMElink #%d) not found.\n", cntrlPtr->name, linkNum);
return (DEV_NO_CONTROLLER);
}
printf ("%s (VMElink #%d) found.\n", cntrlPtr->name, linkNum);
linkInfo = VMEInfo[linkNum] = (VMELinkInfo *)malloc (sizeof (VMELinkInfo));
linkInfo->unit = linkNum;
linkInfo->LocalFlags = 0;
linkInfo->RemoteFlags1 = 0;
linkInfo->RemoteFlags2 = 0 /*DEV_VMELINK_REMOTE_PAUSE_16*/;
linkInfo->regArea = regPtr;
linkInfo->dmaRegs = (DmaRegs *)((char *)regPtr + sizeof (CtrlRegs));
linkInfo->addrMsb = 0x80000000;
linkInfo->state = 0;
linkInfo->vectorNumber = cntrlPtr->vectorNumber;
linkInfo->minDmaSize = DEV_VMELINK_MIN_DMA_SIZE;
linkInfo->position = 0;
sprintf (linkInfo->semName, "VME link 0x%x", linkNum);
Sync_SemInitDynamic (&(linkInfo->mutex), semName);
List_Init (&(linkInfo->reqHdr));
linkInfo->numAttached = 0;
linkInfo->curReq = NULL;
blockHandle = &(linkInfo->handle.blockHandle);
linkInfo->handle.linkInfo = linkInfo;
linkInfo->handle.magic = 0xfaced;
strcpy (linkInfo->name, cntrlPtr->name);
blockHandle->blockIOProc = DevVMElinkBlockIO;
blockHandle->IOControlProc = DevVMElinkBlockIOControl;
blockHandle->releaseProc = DevVMElinkRelease;
blockHandle->minTransferUnit = sizeof (int);
blockHandle->maxTransferSize = DEV_VMELINK_MAX_TRANSFER_SIZE;
/*
* Map in a 64K window so we can use the page register to read or
* write from any VME address in the remote cage.
*/
if (windowPhysAddr[linkNum] != 0) {
linkInfo->smallMap = (Address)VmMach_MapInBigDevice
((void*)windowPhysAddr[linkNum], 0x10000, VMMACH_TYPE_VME32DATA);
if (linkInfo->smallMap == NULL) {
printf ("DevVMElinkInit: couldn't map window.\n");
linkInfo->state |= DEV_VMELINK_STATE_NO_MAP;
} else if (((unsigned)windowPhysAddr[linkNum] & 0xff000000) !=
0xff000000) {
linkInfo->state |= DEV_VMELINK_STATE_VME_A32;
linkInfo->curAddrModifier = 0x0d;
printf ("%s is A32D32 with window at 0x%x mapped into 0x%x\n",
linkInfo->name,windowPhysAddr[linkNum],linkInfo->smallMap);
} else {
linkInfo->curAddrModifier = 0x3d;
printf ("%s is A32D24 with window at 0x%x mapped into 0x%x\n",
linkInfo->name,windowPhysAddr[linkNum],linkInfo->smallMap);
}
} else {
linkInfo->state |= DEV_VMELINK_STATE_NO_MAP;
}
status = DevVMElinkReset (linkInfo, FALSE);
return ((ClientData)(linkInfo));
}
/*
*----------------------------------------------------------------------
*
* DevVMElinkOpen
*
* Open the VME link device. Since there isn't much to keep
* track of, just return a handle for future operations. The
* handle is merely the unit number.
*
* Results:
* Standard Sprite ReturnStatus.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
DevVMElinkOpen (devicePtr, useFlags, notifyToken)
Fs_Device *devicePtr;
int useFlags;
Fs_NotifyToken notifyToken;
{
if (devVMElinkDebug) {
printf ("DevVMElinkOpen: Trying to open unit %d\n", devicePtr->unit);
}
if (devicePtr->unit >= DEV_VMELINK_MAX_BOARDS) {
return (DEV_INVALID_UNIT);
}
devicePtr->data = (ClientData)(VMEInfo[devicePtr->unit]);
if (devicePtr->data == NULL) {
return (DEV_INVALID_UNIT);
}
if (devVMElinkDebug) {
printf ("VMElink: Opened device successfully (unit %d).\n",
devicePtr->unit);
}
return (SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* DevVMElinkRead
*
* Read some bytes from the remote VME into a buffer provided.
* Since this may require playing with the virtual->physical
* mapping, the virtual memory may get rearranged.
*
* Results:
* SUCCESS if read went OK.
*
* Side effects:
* May change mapping registers used by the VME driver.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
DevVMElinkRead (devicePtr, readPtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOParam *readPtr;
Fs_IOReply *replyPtr;
{
DevBlockDeviceRequest blockReq;
ReturnStatus retval;
VMELinkInfo *linkInfo = (VMELinkInfo *)devicePtr->data;
int xferred;
if (devVMElinkDebug) {
printf ("DevVMElinkRead: reading 0x%x bytes at offset 0x%x\n",
readPtr->length, readPtr->offset);
}
blockReq.operation = FS_READ;
blockReq.startAddress = readPtr->offset;
blockReq.bufferLen = readPtr->length;
blockReq.buffer = readPtr->buffer;
retval = Dev_BlockDeviceIOSync
((DevBlockDeviceHandle *)&(linkInfo->handle), &blockReq, &xferred);
replyPtr->flags = FS_READABLE | FS_WRITABLE;
replyPtr->length = xferred;
return (retval);
}
/*
*----------------------------------------------------------------------
*
* DevVMElinkWrite
*
* Write some bytes from a buffer to the remote VME.
* Since this may require playing with the virtual->physical
* mapping, the virtual memory may get rearranged.
*
* Results:
* SUCCESS if write went OK.
*
* Side effects:
* May change mapping registers used by the VME driver.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
DevVMElinkWrite (devicePtr, writePtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOParam *writePtr;
Fs_IOReply *replyPtr;
{
DevBlockDeviceRequest blockReq;
ReturnStatus retval;
VMELinkInfo *linkInfo = (VMELinkInfo *)devicePtr->data;
int xferred;
if (devVMElinkDebug) {
printf ("DevVMElinkWrite: writing 0x%x bytes at offset 0x%x\n",
writePtr->length, writePtr->offset);
}
blockReq.operation = FS_WRITE;
blockReq.startAddress = writePtr->offset;
blockReq.bufferLen = writePtr->length;
blockReq.buffer = writePtr->buffer;
retval = Dev_BlockDeviceIOSync
((DevBlockDeviceHandle *)&(linkInfo->handle), &blockReq, &xferred);
replyPtr->flags = FS_READABLE | FS_WRITABLE;
replyPtr->length = xferred;
return (retval);
}